home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / bin / tasksel < prev    next >
Text File  |  2009-10-22  |  22KB  |  852 lines

  1. #!/usr/bin/perl
  2. # Debian task selector, mark II.
  3. # Copyright 2004-2006 by Joey Hess <joeyh@debian.org>.
  4. # Licensed under the GPL, version 2 or higher.
  5. use Locale::gettext;
  6. use Getopt::Long;
  7. use POSIX qw(floor);
  8. use warnings;
  9. use strict;
  10. textdomain('tasksel');
  11.  
  12. my $debconf_helper="/usr/lib/tasksel/tasksel-debconf";
  13. my $filter_tasks="/usr/lib/tasksel/filter-tasks";
  14. my $disconnect="/usr/lib/tasksel/disconnect";
  15. my $testdir="/usr/lib/tasksel/tests";
  16. my $packagesdir="/usr/lib/tasksel/packages";
  17. my $descdir="/usr/share/tasksel";
  18. my $localdescdir="/usr/local/share/tasksel";
  19. my $statusfile="/var/lib/dpkg/status";
  20. my $infodir="/usr/lib/tasksel/info";
  21. my $testmode=0;
  22.  
  23. sub warning {
  24.     print STDERR "tasksel: @_\n";
  25. }
  26.  
  27. sub error {
  28.     print STDERR "tasksel: @_\n";
  29.     exit 1;
  30. }
  31.  
  32. # Run a shell command except in test mode, and returns its exit code.
  33. # Prints the command in test mode. Parameters should be pre-split for
  34. # system.
  35. sub run {
  36.     if ($testmode) {
  37.         print join(" ", @_)."\n";
  38.         return 0;
  39.     }
  40.     else {
  41.         return system(@_) >> 8;
  42.     }
  43. }
  44.  
  45. sub run_disconnect {
  46.     if ($testmode) {
  47.         print join(" ", @_)."\n";
  48.         return 0;
  49.     }
  50.     else {
  51.         return system($disconnect, @_) >> 8;
  52.     }
  53. }
  54.  
  55. # A list of all available task desc files.
  56. sub list_task_descs {
  57.     return glob("$descdir/*.desc"), glob("$localdescdir/*.desc");
  58. }
  59.  
  60. # Returns a list of hashes; hash values are arrays for multi-line fields.
  61. sub read_task_desc {
  62.     my $desc=shift;
  63.     my @ret;
  64.     open (DESC, "<$desc") || die "read $desc\: $!";
  65.     local $/="\n\n";
  66.     while (<DESC>) {
  67.         my %data;
  68.         my @lines=split("\n");
  69.         while (@lines) {
  70.             my $line=shift(@lines);
  71.             if ($line=~/^([^ ]+):(?: (.*))?/) {
  72.                 my ($key, $value)=($1, $2);
  73.                 $key=lc($key);
  74.                 if (@lines && $lines[0] =~ /^\s+/) {
  75.                     # multi-line field
  76.                     my @values;
  77.                     if (defined $value && length $value) {
  78.                         push @values, $value;
  79.                     }
  80.                     while (@lines && $lines[0] =~ /^\s+(.*)/) {
  81.                         push @values, $1;
  82.                         shift @lines;
  83.                     }
  84.                     $data{$key}=[@values];
  85.                 }
  86.                 else {
  87.                     $data{$key}=$value;
  88.                 }
  89.             }
  90.             else {
  91.                 warning "parse error in stanza $. of $desc";
  92.             }
  93.         }
  94.         if (%data) {
  95.             $data{relevance}=5 unless exists $data{relevance};
  96.             $data{shortdesc}=$data{description}->[0];
  97.             $data{shortdesctrans}=dgettext("debian-tasks", $data{shortdesc});
  98.             push @ret, \%data;
  99.         }
  100.     }
  101.     close DESC;
  102.     return @ret;
  103. }
  104.  
  105. # Loads info for all tasks, and returns a set of task structures.
  106. sub all_tasks {
  107.     my %seen;
  108.     grep { $seen{$_->{task}}++; $seen{$_->{task}} < 2 }
  109.     map { read_task_desc($_) } list_task_descs();
  110. }
  111.  
  112. # Returns a list of all available packages.
  113. sub list_avail {
  114.     my @list;
  115.     # Might be better to use the perl apt bindings, but they are not
  116.     # currently in base.
  117.     open (AVAIL, "apt-cache dumpavail|");
  118.     local $_;
  119.     while (<AVAIL>) {
  120.         chomp;
  121.         if (/^Package: (.*)/) {
  122.             push @list, $1;
  123.         }
  124.     }
  125.     close AVAIL;
  126.     return @list;
  127. }
  128.  
  129. # Returns a list of all installed packages.
  130. sub list_installed {
  131.     my @list;
  132.     local $/="\n\n";
  133.     open (STATUS, $statusfile);
  134.     local $_;
  135.     while (<STATUS>) {
  136.         if (/^Status: .* installed$/m && /Package: (.*)$/m) {
  137.             push @list, $1;
  138.         }
  139.     }
  140.     close STATUS;
  141.     return @list;
  142. }
  143.  
  144. my %avail_pkgs;
  145. # Given a package name, checks to see if it's available. Memoised.
  146. sub package_avail {
  147.     my $package=shift;
  148.     
  149.     if (! %avail_pkgs) {
  150.         foreach my $pkg (list_avail()) {
  151.             $avail_pkgs{$pkg} = 1;
  152.         }
  153.     }
  154.  
  155.     return $avail_pkgs{$package} || package_installed($package);
  156. }
  157.  
  158. my %installed_pkgs;
  159. # Given a package name, checks to see if it's installed. Memoised.
  160. sub package_installed {
  161.     my $package=shift;
  162.     
  163.     if (! %installed_pkgs) {
  164.         foreach my $pkg (list_installed()) {
  165.             $installed_pkgs{$pkg} = 1;
  166.         }
  167.     }
  168.  
  169.     return $installed_pkgs{$package};
  170. }
  171.  
  172. # Given a task hash, checks if its key packages are available.
  173. sub task_avail {
  174.     local $_;
  175.     my $task=shift;
  176.     if (! ref $task->{key}) {
  177.         return 1;
  178.     }
  179.     else {
  180.         foreach my $pkg (@{$task->{key}}) {
  181.             if (! package_avail($pkg)) {
  182.                 return 0;
  183.             }
  184.         }
  185.         return 1;
  186.     }
  187. }
  188.  
  189. # Given a task hash, checks to see if it is already installed.
  190. # (All of its key packages must be installed.)
  191. sub task_installed {
  192.     local $_;
  193.     my $task=shift;
  194.     if (! ref $task->{key}) {
  195.         return 0; # can't tell with no key packages
  196.     }
  197.     else {
  198.         foreach my $pkg (@{$task->{key}}) {
  199.             if (! package_installed($pkg)) {
  200.                 return 0;
  201.             }
  202.         }
  203.         return 1;
  204.     }
  205. }
  206.  
  207. # Given task hash, returns a list of all available packages in the task.
  208. # If the aptitude_tasks or apt_get_tasks parameter is true, then it does
  209. # not expand tasks that aptitude/apt-get knows about, and just returns
  210. # aptitude/apt-get task syntax for those.
  211. sub task_packages {
  212.     my $task=shift;
  213.     my $aptitude_tasks=shift;
  214.     my $apt_get_tasks=shift;
  215.     
  216.     my %list;
  217.  
  218.     # key packages are always included
  219.     if (ref $task->{key}) {
  220.         map { $list{$_}=1 } @{$task->{key}};
  221.     }
  222.         
  223.     if ($task->{packages} eq 'task-fields') {
  224.         # task-fields method is built-in for speed and to support
  225.         # aptitude/apt-get task definitions
  226.         if ($aptitude_tasks) {
  227.             return '~t^'.$task->{task}.'$';
  228.         }
  229.         elsif ($apt_get_tasks) {
  230.             return $task->{task}.'^';
  231.         }
  232.         else {
  233.             local $/="\n\n";
  234.             open (AVAIL, "apt-cache dumpavail|");
  235.             while (<AVAIL>) {
  236.                 if (/^Task: (.*)/m) {
  237.                     my @tasks=split(", ", $1);
  238.                     if (grep { $_ eq $task->{task} } @tasks) { 
  239.                         $list{$1}=1 if /^Package: (.*)/m;
  240.                     }
  241.                 }
  242.             }
  243.             close AVAIL;
  244.         }
  245.     }
  246.     elsif ($task->{packages} eq 'standard') {
  247.         # standard method is built in since it cannot easily be
  248.         # implemented externally.
  249.         #return "~pstandard", "~prequired", "~pimportant";
  250.         # Unfortunately, this needs aptitude, not apt-get, and we
  251.         # have to use apt-get for other reasons ...
  252.         die "'Packages: standard' not supported by this version of tasksel\n";
  253.     }
  254.     elsif ($task->{packages} eq 'manual') {
  255.         # manual package selection is a special case
  256.         return;
  257.     }
  258.     else {
  259.         # external method
  260.         my ($method, @params);
  261.         if (ref $task->{packages}) {
  262.             @params=@{$task->{packages}};
  263.             $method=shift @params;
  264.         }
  265.         else {
  266.             $method=$task->{packages};
  267.         }
  268.         
  269.         map { $list{$_}=1 }
  270.             grep { package_avail($_) }
  271.             split(' ', `$packagesdir/$method $task->{task} @params`);
  272.     }
  273.  
  274.     return keys %list;
  275. }
  276.  
  277. # Calculate the list of packages to remove, taking care not to include
  278. # packages that are in other tasks that are still selected.
  279. sub task_packages_remove {
  280.     my $tasks_remove = shift;
  281.  
  282.     my %list;
  283.  
  284.     foreach my $task (@$tasks_remove) {
  285.         $list{$_} = 1 foreach task_packages($task, 0, 0);
  286.     }
  287.     return keys %list unless %list;
  288.     foreach my $task (grep { ! $_->{_display} || $_->{_selected} } @_) {
  289.         delete $list{$_} foreach task_packages($task, 0, 0);
  290.     }
  291.  
  292.     return keys %list;
  293. }
  294.  
  295. # Given a list of task hashes, adjust their statuses according to
  296. # tasksel/force-tasks, tasksel/limit-tasks, and tasksel/skip-tasks.
  297. sub filter_tasks {
  298.     my %tasks=map { $_->{task} => $_ } @_;
  299.     my $tmpfile=`tempfile`;
  300.     chomp $tmpfile;
  301.     my $ret=system($filter_tasks, $tmpfile, sort keys %tasks) >> 8;
  302.     if ($ret != 0) {
  303.         error "filter-tasks failed to run";
  304.     }
  305.     open(IN, "<$tmpfile") or return;
  306.     for my $line (<IN>) {
  307.         chomp $line;
  308.         $line=~/(.*?) (install|skip)/ or next;
  309.         my $name=$1;
  310.         my $action=$2;
  311.         if ($action eq 'install') {
  312.             $tasks{$name}{_display} = 0;
  313.             $tasks{$name}{_install} = 1;
  314.         }
  315.         elsif ($action eq 'skip') {
  316.             $tasks{$name}{_display} = 0;
  317.             $tasks{$name}{_install} = 0;
  318.         }
  319.     }
  320.     close IN;
  321.     unlink $tmpfile;
  322. }
  323.  
  324. # Given a task hash, runs any test program specified in its data, and sets
  325. # the _display and _install fields to 1 or 0 depending on its result.
  326. sub task_test {
  327.     my $task=shift;
  328.     my $new_install=shift;
  329.     $task->{_display} = shift; # default
  330.     $task->{_install} = shift; # default
  331.     $ENV{NEW_INSTALL}=$new_install if defined $new_install;
  332.     foreach my $test (grep /^test-.*/, keys %$task) {
  333.         $test=~s/^test-//;
  334.         if (-x "$testdir/$test") {
  335.             my $ret=system("$testdir/$test", $task->{task}, split " ", $task->{"test-$test"}) >> 8;
  336.             if ($ret == 0) {
  337.                 $task->{_display} = 0;
  338.                 $task->{_install} = 1;
  339.             }
  340.             elsif ($ret == 1) {
  341.                 $task->{_display} = 0;
  342.                 $task->{_install} = 0;
  343.             }
  344.             elsif ($ret == 2) {
  345.                 $task->{_display} = 1;
  346.                 $task->{_install} = 1;
  347.             }
  348.             elsif ($ret == 3) {
  349.                 $task->{_display} = 1;
  350.                 $task->{_install} = 0;
  351.             }
  352.         }
  353.     }
  354.     
  355.     delete $ENV{NEW_INSTALL};
  356.     return $task;
  357. }
  358.  
  359. # Hides a task if it enhances other tasks. Don't mark it not to be installed
  360. # here; this will be taken care of later outside new-install mode, and in
  361. # new-install mode we may want to apply a test to an enhancing task that
  362. # installs it by default.
  363. sub hide_enhancing_tasks {
  364.     my $task=shift;
  365.     if (exists $task->{enhances} && length $task->{enhances}) {
  366.         $task->{_display} = 0;
  367.     }
  368.     return $task;
  369. }
  370.  
  371. # Converts a list of tasks into a debconf list of their short descriptions.
  372. sub task_to_debconf {
  373.     my $field = shift;
  374.     join ", ", map {
  375.         my $desc=$_->{$field};
  376.         if ($desc=~/, /) {
  377.             warning("task ".$_->{task}." contains a comma in its short description: \"$desc\"");
  378.         }
  379.         $desc;
  380.     } @_;
  381. }
  382.  
  383. # Given a first parameter that is a debconf list of short descriptions of
  384. # tasks, or a dependency style list of task names, and then a list of task
  385. # hashes, returns a list of hashes for all the tasks in the list.
  386. sub list_to_tasks {
  387.     my $list=shift;
  388.     my %desc_to_task = map { $_->{shortdesc} => $_, $_->{task} => $_ } @_;
  389.     return grep { defined } map { $desc_to_task{$_} } split ", ", $list;
  390. }
  391.  
  392. # Orders a list of tasks for display.
  393. sub order_for_display {
  394.     sort {
  395.         $b->{relevance} <=> $a->{relevance}
  396.                       || 0 ||
  397.           $a->{section} cmp $b->{section}
  398.                       || 0 ||
  399.             $a->{shortdesc} cmp $b->{shortdesc}
  400.     } @_;
  401. }
  402.  
  403. # Given a set of tasks and a name, returns the one with that name.
  404. sub name_to_task {
  405.     my $name=shift;
  406.     return (grep { $_->{task} eq $name } @_)[0];
  407. }
  408.  
  409. sub find_task_script {
  410.     my $task=shift;
  411.     my $script=shift;
  412.  
  413.     my $path="$infodir/$task.$script";
  414.     if (-e $path && -x _) {
  415.         return $path;
  416.     } else {
  417.         return undef;
  418.     }
  419. }
  420.  
  421. sub run_task_script {
  422.     my $path=pop;
  423.     my @prefix=@_;
  424.  
  425.     my $ret=run(@prefix, $path);
  426.     if ($ret != 0) {
  427.         warning("$path exited with nonzero code $ret");
  428.         return 0;
  429.     }
  430.     return 1;
  431. }
  432.  
  433. sub debconf_apt_command {
  434.     my $dap=shift;
  435.     my $from=shift;
  436.     my $to=shift;
  437.     my $options=shift;
  438.  
  439.     my @cmd=($dap, '--from', $from, '--to', $to);
  440.     push @cmd, @$options if defined $options;
  441.     push @cmd, '--';
  442.     return @cmd;
  443. }
  444.  
  445. # Works out the region of a progress bar needed for a given invocation of
  446. # debconf-apt-progress, and returns the appropriate debconf-apt-progress
  447. # command.
  448. sub task_region {
  449.     my $dap=shift;
  450.     my $start=shift;
  451.     my $end=shift;
  452.     my $options=shift;
  453.     my $cur_region=shift;
  454.     my $step_region=shift;
  455.     my $num_regions=shift;
  456.  
  457.     if (defined $start and defined $end and $num_regions) {
  458.         my $from=floor($start + ($end - $start) * $cur_region / $num_regions + 0.5);
  459.         my $to=floor($start + ($end - $start) * ($cur_region + $step_region) / $num_regions + 0.5);
  460.         return debconf_apt_command($dap, $from, $to, $options);
  461.     } else {
  462.         return;
  463.     }
  464. }
  465.  
  466. sub usage {
  467.     print STDERR gettext(q{Usage:
  468. tasksel install <task>
  469. tasksel remove <task>
  470. tasksel [options]
  471.     -t, --test          test mode; don't really do anything
  472.         --new-install   automatically install some tasks
  473.         --list-tasks    list tasks that would be displayed and exit
  474.         --task-packages list available packages in a task
  475.         --task-desc     returns the description of a task
  476. });
  477. }
  478.  
  479. # Process command line options and return them in a hash.
  480. sub getopts {
  481.     my %ret;
  482.     Getopt::Long::Configure ("bundling");
  483.     if (! GetOptions(\%ret, "test|t", "new-install", "list-tasks",
  484.            "task-packages=s@", "task-desc=s",
  485.            "section=s",
  486.            "debconf-apt-from=i", "debconf-apt-to=i",
  487.            "debconf-apt-progress=s")) {
  488.         usage();
  489.         exit(1);
  490.     }
  491.     # Special case apt-like syntax.
  492.     if (@ARGV && $ARGV[0] eq "install") {
  493.         shift @ARGV;
  494.         $ret{install} = shift @ARGV;
  495.     }
  496.     if (@ARGV && $ARGV[0] eq "remove") {
  497.         shift @ARGV;
  498.         $ret{remove} = shift @ARGV;
  499.     }
  500.     if (@ARGV) {
  501.         usage();
  502.         exit 1;
  503.     }
  504.     $testmode=1 if $ret{test}; # set global
  505.     return %ret;
  506. }
  507.  
  508. sub main {
  509.     my %options=getopts();
  510.     my @tasks_remove;
  511.     my @tasks_install;
  512.  
  513.     # Options that output stuff and don't need a full processed list of
  514.     # tasks.
  515.     if (exists $options{"task-packages"}) {
  516.         my @tasks=all_tasks();
  517.         foreach my $taskname (@{$options{"task-packages"}}) {
  518.             my $task=name_to_task($taskname, @tasks);
  519.             if ($task) {
  520.                 print "$_\n" foreach task_packages($task);
  521.             }
  522.         }
  523.         exit(0);
  524.     }
  525.     elsif ($options{"task-desc"}) {
  526.         my $task=name_to_task($options{"task-desc"}, all_tasks());
  527.         if ($task) {
  528.             my $extdesc=join(" ", @{$task->{description}}[1..$#{$task->{description}}]);
  529.             print dgettext("debian-tasks", $extdesc)."\n";
  530.             exit(0);
  531.         }
  532.         else {
  533.             exit(1);
  534.         }
  535.     }
  536.  
  537.     # This is relatively expensive, get the full list of available tasks and
  538.     # mark them.
  539.     my @tasks=map { hide_enhancing_tasks($_) } map { task_test($_, $options{"new-install"}, 1, 0) }
  540.               grep { task_avail($_) } all_tasks();
  541.     if ($options{"new-install"}) {
  542.         filter_tasks(@tasks);
  543.     }
  544.     
  545.     # Limit the tasks shown to a single section
  546.     if ($options{section}) { 
  547.         @tasks = grep { $options{section} eq $_->{section} } @tasks; 
  548.     }
  549.  
  550.     if ($options{"list-tasks"}) {
  551.         map { $_->{_installed} = task_installed($_) } @tasks;
  552.         print "".($_->{_installed} ? "i" : "u")." ".$_->{task}."\t".$_->{shortdesc}."\n"
  553.             foreach order_for_display(grep { $_->{_display} } @tasks);
  554.         exit(0);
  555.     }
  556.     
  557.     if (! $options{"new-install"}) {
  558.         # Don't install hidden tasks if this is not a new install.
  559.         map { $_->{_install} = 0 } grep { $_->{_display} == 0 } @tasks;
  560.     }
  561.     if ($options{"install"}) {
  562.         my $task=name_to_task($options{"install"}, @tasks);
  563.         $task->{_install} = 1 if $task;
  564.     }
  565.     if ($options{"remove"}) {
  566.         my $task=name_to_task($options{"remove"}, @tasks);
  567.         push @tasks_remove, $task;
  568.     }
  569.     
  570.     # The interactive bit.
  571.     my $interactive=0;
  572.     my @list = order_for_display(grep { $_->{_display} == 1 } @tasks);
  573.     if (@list && ! $options{install} && ! $options{remove}) {
  574.         $interactive=1;
  575.         if (! $options{"new-install"}) {
  576.             # Find tasks that are already installed.
  577.             map { $_->{_installed} = task_installed($_) } @list;
  578.             # Don't install new tasks unless manually selected.
  579.             map { $_->{_install} = 0 } @list;
  580.         }
  581.         else {
  582.             # Assume that no tasks are installed, to ensure
  583.             # that complete tasks get installed on new
  584.             # installs.
  585.             map { $_->{_installed} = 0 } @list;
  586.         }
  587.         my $question="tasksel/tasks";
  588.         if ($options{"new-install"}) {
  589.             $question="tasksel/first";
  590.         }
  591.         my @default = grep { $_->{_display} == 1 && ($_->{_install} == 1 || $_->{_installed} == 1) } @tasks;
  592.         my $tmpfile=`tempfile`;
  593.         chomp $tmpfile;
  594.  
  595.         # Canonicalise $question's value first, so that it will be
  596.         # displayed properly if preseeded unseen.
  597.         my $ret=system($debconf_helper, "--get", $tmpfile,
  598.             $question) >> 8;
  599.         if ($ret != 0) {
  600.             error "debconf failed to run";
  601.         }
  602.         open(IN, "<$tmpfile");
  603.         $ret=<IN>;
  604.         if (! defined $ret) {
  605.             die "tasksel canceled\n";
  606.         }
  607.         chomp $ret;
  608.         close IN;
  609.  
  610.         if ($ret ne "") {
  611.             my $new_value = task_to_debconf("shortdesc",
  612.                 list_to_tasks($ret, @list));
  613.             $ret=system($debconf_helper, "--set", $question,
  614.                 $new_value) >> 8;
  615.             if ($ret != 0) {
  616.                 error "debconf failed to run";
  617.             }
  618.         }
  619.  
  620.         $ret=system($debconf_helper, $tmpfile,
  621.             task_to_debconf("shortdesc", @list),
  622.             task_to_debconf("shortdesctrans", @list),
  623.             task_to_debconf("shortdesc", @default),
  624.             $question) >> 8;
  625.         if ($ret == 30) {
  626.             exit 10; # back up
  627.         }
  628.         elsif ($ret != 0) {
  629.             error "debconf failed to run";
  630.         }
  631.         open(IN, "<$tmpfile");
  632.         $ret=<IN>;
  633.         if (! defined $ret) {
  634.             die "tasksel canceled\n";
  635.         }
  636.         chomp $ret;
  637.         close IN;
  638.         unlink $tmpfile;
  639.         
  640.         # Set _install flags based on user selection.
  641.         map { $_->{_install} = 0 } @list;
  642.         foreach my $task (list_to_tasks($ret, @tasks)) {
  643.             if (! $task->{_installed}) {
  644.                 $task->{_install} = 1;
  645.             }
  646.             $task->{_selected} = 1;
  647.         }
  648.         foreach my $task (@list) {
  649.             if (! $task->{_selected} && $task->{_installed}) {
  650.                 push @tasks_remove, $task;
  651.             }
  652.         }
  653.     }
  654.  
  655.     # If an enhancing task is already marked for
  656.     # install, probably by preseeding, mark the tasks
  657.     # it enhances for install.
  658.     foreach my $task (grep { $_->{_install} && exists $_->{enhances} &&
  659.                              length $_->{enhances} } @tasks) {
  660.         # Ignore any disjunctive sets. We don't know which
  661.         # element(s) we should use, so if the user didn't select any
  662.         # it's better to just leave them alone.
  663.         my $simple_enhances=join ", ", grep { ! /\|/ } split ", ", $task->{enhances};
  664.         map { $_->{_install}=1 } list_to_tasks($simple_enhances, @tasks);
  665.     }
  666.  
  667.     # Select enhancing tasks for install.
  668.     # XXX FIXME ugly hack -- loop until enhances settle to handle
  669.     # chained enhances. This is sloow and could loop forever if
  670.     # there's a cycle.
  671.     my $enhances_needswork=1;
  672.     while ($enhances_needswork) {
  673.         $enhances_needswork=0;
  674.         foreach my $task (grep { ! $_->{_install} && exists $_->{enhances} &&
  675.                                  length $_->{enhances} } @tasks) {
  676.             my @ordeps=split ", ", $task->{enhances};
  677.             if (@ordeps) {
  678.                 my $orig_state=$task->{_install};
  679.  
  680.                 # Mark enhancing tasks for install if their
  681.                 # dependencies are met and their test fields
  682.                 # mark them for install.
  683.                 $ENV{TESTING_ENHANCER}=1;
  684.                 task_test($task, $options{"new-install"}, 0, 1);
  685.                 delete $ENV{TESTING_ENHANCER};
  686.                 foreach my $ordep (@ordeps) {
  687.                     my $satisfied=0;
  688.                     foreach my $dep (list_to_tasks(join(", ", split " | ", $ordep), @tasks)) {
  689.                         if ($dep->{_install}) {
  690.                             $satisfied=1;
  691.                         }
  692.                     }
  693.                     if (! $satisfied) {
  694.                         $task->{_install} = 0;
  695.                     }
  696.                 }
  697.  
  698.                 if ($task->{_install} != $orig_state) {
  699.                     $enhances_needswork=1;
  700.                 }
  701.             }
  702.         }
  703.     }
  704.  
  705.     # Add tasks to install and see if any selected task requires manual
  706.     # selection.
  707.     my $manual_selection=0;
  708.     foreach my $task (grep { $_->{_install} } @tasks) {
  709.         push @tasks_install, $task;
  710.         if ($task->{packages} eq 'manual') {
  711.             if (-x "/usr/bin/aptitude") {
  712.                 $manual_selection=1;
  713.             } else {
  714.                 warning "task $task requires aptitude";
  715.             }
  716.         }
  717.     }
  718.     
  719.     my $dap;
  720.     my ($dap_from, $dap_to);
  721.     my @dap_options;
  722.     my (@aptitude, @apt);
  723.     if (-x "/usr/bin/debconf-apt-progress") {
  724.         $dap="debconf-apt-progress";
  725.         if (exists $options{'debconf-apt-from'} and
  726.             exists $options{'debconf-apt-to'}) {
  727.             $dap_from=$options{'debconf-apt-from'};
  728.             $dap_to=$options{'debconf-apt-to'};
  729.         }
  730.         push @dap_options, split(' ', $options{'debconf-apt-progress'})
  731.             if exists $options{'debconf-apt-progress'};
  732.         @aptitude=qw{aptitude -q};
  733.         @apt=qw{apt-get -q};
  734.     }
  735.     else {
  736.         @aptitude="aptitude";
  737.         @apt="apt-get";
  738.     }
  739.  
  740.     # We arbitrarily allocate 5% of the progress bar to task scripts,
  741.     # divided equally among all the scripts. (If you change this, the
  742.     # new percentage should divide equally into 100.)
  743.     my $num_regions=0;
  744.     foreach my $task (@tasks_remove) {
  745.         if (defined find_task_script($task->{task}, "prerm")) {
  746.             ++$num_regions;
  747.         }
  748.         if (defined find_task_script($task->{task}, "postrm")) {
  749.             ++$num_regions;
  750.         }
  751.     }
  752.     foreach my $task (@tasks_install) {
  753.         if (defined find_task_script($task->{task}, "preinst")) {
  754.             ++$num_regions;
  755.         }
  756.         if (defined find_task_script($task->{task}, "postinst")) {
  757.             ++$num_regions;
  758.         }
  759.     }
  760.     $num_regions*=20;
  761.     my $cur_region=0;
  762.     
  763.     my @task_packages_remove = task_packages_remove(\@tasks_remove, @tasks);
  764.  
  765.     # And finally, act on selected tasks.
  766.     if (@tasks_install || @task_packages_remove || $manual_selection) {
  767.         my (@aptitude_args, @apt_args);
  768.         my @args = map { "$_-" } @task_packages_remove;
  769.         push @aptitude_args, @args;
  770.         push @apt_args, @args;
  771.         foreach my $task (@tasks_remove) {
  772.             my $path=find_task_script($task->{task}, "prerm");
  773.             if (defined $path) {
  774.                 my @prefix=task_region($dap, $dap_from, $dap_to, \@dap_options, $cur_region++, 1, $num_regions);
  775.                 run_task_script(@prefix, $path);
  776.             }
  777.         }
  778.         foreach my $task (@tasks_install) {
  779.             push @aptitude_args, task_packages($task, 1, 0);
  780.             push @apt_args, task_packages($task, 0, 1);
  781.             my $path=find_task_script($task->{task}, "preinst");
  782.             if (defined $path) {
  783.                 my @prefix=task_region($dap, $dap_from, $dap_to, \@dap_options, $cur_region++, 1, $num_regions);
  784.                 run_task_script(@prefix, $path);
  785.             }
  786.         }
  787.         my @prefix;
  788.         if (defined $dap_from and defined $dap_to) {
  789.             if ($num_regions) {
  790.                 # The main apt-get run takes up the
  791.                 # remaining 95% of the progress bar.
  792.                 @prefix=task_region($dap, $dap_from, $dap_to, \@dap_options, $cur_region, $num_regions * 0.95, $num_regions);
  793.                 $cur_region=floor($cur_region + $num_regions * 0.95 + 0.5);
  794.             } else {
  795.                 @prefix=debconf_apt_command($dap, $dap_from, $dap_to, \@dap_options);
  796.             }
  797.         } elsif (defined $dap) {
  798.             @prefix=($dap, @dap_options, '--');
  799.         }
  800.         my $ret;
  801.         # If the user selected no other tasks and manual package
  802.         # selection, run aptitude w/o the --visual-preview parameter.
  803.         if (! @aptitude_args && $manual_selection) {
  804.             $ret=run_disconnect("aptitude", "--schedule-only");
  805.             if ($ret != 0) {
  806.                 error gettext("aptitude failed")." ($ret)";
  807.             } else {
  808.                 $ret=run(@prefix, @aptitude, "-y", "install");
  809.                 if ($ret != 0) {
  810.                     error gettext("aptitude failed")." ($ret)";
  811.                 }
  812.             }
  813.         }
  814.         else {
  815.             if ($manual_selection) {
  816.                 $ret=run(@aptitude, "-y", "--schedule-only", "install", @aptitude_args);
  817.             } else {
  818.                 $ret=run(@prefix, @apt, "-y", "install", @apt_args);
  819.             }
  820.             if ($ret != 0) {
  821.                 error gettext("aptitude failed")." ($ret)";
  822.             } elsif ($manual_selection) {
  823.                 $ret=run_disconnect(@aptitude, "--schedule-only");
  824.                 if ($ret != 0) {
  825.                     error gettext("aptitude failed")." ($ret)";
  826.                 } else {
  827.                     $ret=run(@prefix, @aptitude, "-y", "install");
  828.                     if ($ret != 0) {
  829.                         error gettext("aptitude failed")." ($ret)";
  830.                     }
  831.                 }
  832.             }
  833.         }
  834.         foreach my $task (@tasks_remove) {
  835.             my $path=find_task_script($task->{task}, "postrm");
  836.             if (defined $path) {
  837.                 my @prefix=task_region($dap, $dap_from, $dap_to, \@dap_options, $cur_region++, 1, $num_regions);
  838.                 run_task_script(@prefix, $path);
  839.             }
  840.         }
  841.         foreach my $task (@tasks_install) {
  842.             my $path=find_task_script($task->{task}, "postinst");
  843.             if (defined $path) {
  844.                 my @prefix=task_region($dap, $dap_from, $dap_to, \@dap_options, $cur_region++, 1, $num_regions);
  845.                 run_task_script(@prefix, $path);
  846.             }
  847.         }
  848.     }
  849. }
  850.  
  851. main();
  852.